package com.aspire.aimini.service.impl;

import com.aspire.aimini.constant.ErrorCode;
import com.aspire.aimini.exception.BusinessException;
import com.aspire.aimini.model.dto.MaterialDTO;
import com.aspire.aimini.model.dto.TagDTO;
import com.aspire.aimini.model.entity.Material;
import com.aspire.aimini.model.entity.MaterialTag;
import com.aspire.aimini.model.entity.Tag;
import com.aspire.aimini.model.query.MaterialQuery;
import com.aspire.aimini.repository.MaterialRepository;
import com.aspire.aimini.repository.MaterialTagRepository;
import com.aspire.aimini.repository.TagRepository;
import com.aspire.aimini.service.MaterialService;
import com.aspire.aimini.util.FileUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 素材Service实现类
 *
 * @author wangguiyou
 * @date 2024-01-01
 * @description 素材业务逻辑实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MaterialServiceImpl implements MaterialService {

    private final MaterialRepository materialRepository;
    private final MaterialTagRepository materialTagRepository;
    private final TagRepository tagRepository;
    private final FileUtil fileUtil;

    @Override
    @Transactional
    public List<MaterialDTO> uploadMaterials(List<MultipartFile> files, String description, Long userId) {
        if (CollectionUtils.isEmpty(files)) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "上传文件不能为空");
        }

        List<MaterialDTO> result = new ArrayList<>();
        for (MultipartFile file : files) {
            try {
                // 验证文件
                fileUtil.validateFile(file);

                // 保存文件
                String filePath = fileUtil.saveFile(file, userId);
                String thumbnailPath = fileUtil.generateThumbnail(filePath);

                // 创建素材记录
                Material material = new Material();
                material.setUserId(userId);
                material.setFileName(file.getOriginalFilename());
                material.setFilePath(filePath);
                material.setFileSize(file.getSize());
                material.setFileType(fileUtil.getFileType(file));
                material.setMimeType(file.getContentType());
                material.setThumbnailPath(thumbnailPath);
                material.setSourceType("upload");
                material.setDescription(description);
                material.setStatus(1);
                material.setCreateTime(LocalDateTime.now());
                material.setUpdateTime(LocalDateTime.now());

                // 如果是图片，获取尺寸信息
                if (fileUtil.isImage(file)) {
                    Map<String, Integer> dimensions = fileUtil.getImageDimensions(filePath);
                    material.setWidth(dimensions.get("width"));
                    material.setHeight(dimensions.get("height"));
                }

                materialRepository.insert(material);

                // 转换为DTO
                MaterialDTO dto = convertToDTO(material);
                result.add(dto);

            } catch (Exception e) {
                log.error("上传文件失败: {}", file.getOriginalFilename(), e);
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "文件上传失败: " + e.getMessage());
            }
        }

        return result;
    }

    @Override
    public IPage<MaterialDTO> getMaterialPage(MaterialQuery query) {
        Page<Material> page = new Page<>(query.getPage(), query.getSize());
        IPage<Material> materialPage = materialRepository.selectMaterialPage(
                page, query.getUserId(), query.getKeyword(), query.getTags(),
                query.getSourceType(), query.getFileType(), query.getIsFavorite(),
                query.getIsArchived(), query.getSortBy(), query.getSortOrder(),
                query.getStartTime(), query.getEndTime());

        return materialPage.convert(this::convertToDTO);
    }

    @Override
    public MaterialDTO getMaterialById(Long id, Long userId) {
        Material material = materialRepository.selectOne(
                new LambdaQueryWrapper<Material>()
                        .eq(Material::getId, id)
                        .eq(Material::getUserId, userId)
                        .eq(Material::getStatus, 1));

        if (material == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "素材不存在");
        }

        // 增加查看次数
        materialRepository.incrementViewCount(id);

        return convertToDTO(material);
    }

    @Override
    @Transactional
    public MaterialDTO updateMaterial(Long id, String description, Boolean isFavorite, Boolean isArchived,
            Long userId) {
        Material material = materialRepository.selectOne(
                new LambdaQueryWrapper<Material>()
                        .eq(Material::getId, id)
                        .eq(Material::getUserId, userId)
                        .eq(Material::getStatus, 1));

        if (material == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "素材不存在");
        }

        // 更新字段
        if (StringUtils.hasText(description)) {
            material.setDescription(description);
        }
        if (isFavorite != null) {
            material.setIsFavorite(isFavorite);
        }
        if (isArchived != null) {
            material.setIsArchived(isArchived);
        }
        material.setUpdateTime(LocalDateTime.now());

        materialRepository.updateById(material);

        return convertToDTO(material);
    }

    @Override
    @Transactional
    public void deleteMaterial(Long id, Long userId) {
        Material material = materialRepository.selectOne(
                new LambdaQueryWrapper<Material>()
                        .eq(Material::getId, id)
                        .eq(Material::getUserId, userId));

        if (material == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "素材不存在");
        }

        // 删除文件
        try {
            fileUtil.deleteFile(material.getFilePath());
            if (StringUtils.hasText(material.getThumbnailPath())) {
                fileUtil.deleteFile(material.getThumbnailPath());
            }
        } catch (Exception e) {
            log.warn("删除文件失败: {}", material.getFilePath(), e);
        }

        // 删除数据库记录
        materialRepository.deleteById(id);

        // 删除标签关联
        materialTagRepository.deleteByMaterialId(id);
    }

    @Override
    @Transactional
    public void batchDeleteMaterials(List<Long> ids, Long userId) {
        if (CollectionUtils.isEmpty(ids)) {
            return;
        }

        for (Long id : ids) {
            deleteMaterial(id, userId);
        }
    }

    @Override
    @Transactional
    public void addTagsToMaterial(Long materialId, List<Long> tagIds, Long userId) {
        // 验证素材存在
        Material material = materialRepository.selectOne(
                new LambdaQueryWrapper<Material>()
                        .eq(Material::getId, materialId)
                        .eq(Material::getUserId, userId)
                        .eq(Material::getStatus, 1));

        if (material == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "素材不存在");
        }

        if (CollectionUtils.isEmpty(tagIds)) {
            return;
        }

        // 批量添加标签关联
        List<MaterialTag> materialTags = new ArrayList<>();
        for (Long tagId : tagIds) {
            // 检查是否已存在
            if (!materialTagRepository.existsByMaterialIdAndTagId(materialId, tagId)) {
                MaterialTag materialTag = new MaterialTag();
                materialTag.setMaterialId(materialId);
                materialTag.setTagId(tagId);
                materialTag.setSource("manual");
                materialTag.setCreateTime(LocalDateTime.now());
                materialTags.add(materialTag);

                // 增加标签使用次数
                tagRepository.incrementUseCount(tagId);
            }
        }

        if (!CollectionUtils.isEmpty(materialTags)) {
            materialTagRepository.batchInsert(materialTags);
        }
    }

    @Override
    @Transactional
    public void removeTagsFromMaterial(Long materialId, List<Long> tagIds, Long userId) {
        // 验证素材存在
        Material material = materialRepository.selectOne(
                new LambdaQueryWrapper<Material>()
                        .eq(Material::getId, materialId)
                        .eq(Material::getUserId, userId)
                        .eq(Material::getStatus, 1));

        if (material == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "素材不存在");
        }

        if (CollectionUtils.isEmpty(tagIds)) {
            return;
        }

        // 批量删除标签关联
        for (Long tagId : tagIds) {
            materialTagRepository.deleteByMaterialIdAndTagId(materialId, tagId);
            // 减少标签使用次数
            tagRepository.decrementUseCount(tagId);
        }
    }

    @Override
    public void incrementViewCount(Long id, Long userId) {
        materialRepository.incrementViewCount(id);
    }

    @Override
    public void incrementDownloadCount(Long id, Long userId) {
        materialRepository.incrementDownloadCount(id);
    }

    @Override
    public IPage<MaterialDTO> getFavoriteMaterials(Long userId, Integer page, Integer size) {
        Page<Material> materialPage = new Page<>(page, size);
        IPage<Material> result = materialRepository.selectFavoritesByUserId(materialPage, userId);
        return result.convert(this::convertToDTO);
    }

    @Override
    public List<MaterialDTO> getRecentMaterials(Long userId, Integer limit) {
        List<Material> materials = materialRepository.selectRecentMaterials(userId, limit);
        return materials.stream().map(this::convertToDTO).collect(Collectors.toList());
    }

    @Override
    public IPage<MaterialDTO> getMaterialsByTags(List<Long> tagIds, Long userId, Integer page, Integer size) {
        if (CollectionUtils.isEmpty(tagIds)) {
            return new Page<>();
        }

        List<Long> materialIds = materialTagRepository.selectMaterialIdsByTagIdsIntersection(tagIds);
        if (CollectionUtils.isEmpty(materialIds)) {
            return new Page<>();
        }

        Page<Material> materialPage = new Page<>(page, size);
        IPage<Material> result = materialRepository.selectPage(
                materialPage,
                new LambdaQueryWrapper<Material>()
                        .in(Material::getId, materialIds)
                        .eq(Material::getUserId, userId)
                        .eq(Material::getStatus, 1)
                        .orderByDesc(Material::getCreateTime));

        return result.convert(this::convertToDTO);
    }

    @Override
    public List<MaterialDTO> findSimilarMaterials(Long materialId, Long userId, Integer limit) {
        List<Material> materials = materialRepository.selectSimilarMaterials(materialId, userId, limit);
        return materials.stream().map(this::convertToDTO).collect(Collectors.toList());
    }

    @Override
    public Map<String, Object> getUserMaterialStats(Long userId) {
        Map<String, Object> stats = new HashMap<>();
        stats.put("totalCount", materialRepository.countByUserId(userId));
        stats.put("totalSize", materialRepository.sumStorageByUserId(userId));
        stats.put("favoriteCount", materialRepository.countFavoritesByUserId(userId));
        stats.put("archivedCount", materialRepository.countArchivedByUserId(userId));
        return stats;
    }

    @Override
    public List<Map<String, Object>> countByFileType(Long userId) {
        return materialRepository.countByFileType(userId);
    }

    @Override
    public List<Map<String, Object>> countBySourceType(Long userId) {
        return materialRepository.countBySourceType(userId);
    }

    @Override
    public int cleanExpiredTempFiles() {
        LocalDateTime expiredTime = LocalDateTime.now().minusDays(7);
        return materialRepository.cleanExpiredTempFiles(expiredTime);
    }

    @Override
    public String generateThumbnail(Long materialId, Long userId) {
        Material material = materialRepository.selectOne(
                new LambdaQueryWrapper<Material>()
                        .eq(Material::getId, materialId)
                        .eq(Material::getUserId, userId)
                        .eq(Material::getStatus, 1));

        if (material == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "素材不存在");
        }

        try {
            String thumbnailPath = fileUtil.generateThumbnail(material.getFilePath());
            material.setThumbnailPath(thumbnailPath);
            material.setUpdateTime(LocalDateTime.now());
            materialRepository.updateById(material);
            return thumbnailPath;
        } catch (Exception e) {
            log.error("生成缩略图失败: {}", material.getFilePath(), e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "生成缩略图失败");
        }
    }

    @Override
    public int batchGenerateThumbnails(List<Long> materialIds, Long userId) {
        int successCount = 0;
        for (Long materialId : materialIds) {
            try {
                generateThumbnail(materialId, userId);
                successCount++;
            } catch (Exception e) {
                log.warn("生成缩略图失败: materialId={}", materialId, e);
            }
        }
        return successCount;
    }

    @Override
    public boolean checkFileExists(String fileHash, Long userId) {
        return materialRepository.selectCount(
                new LambdaQueryWrapper<Material>()
                        .eq(Material::getFileHash, fileHash)
                        .eq(Material::getUserId, userId)
                        .eq(Material::getStatus, 1)) > 0;
    }

    @Override
    public String getDownloadUrl(Long materialId, Long userId) {
        Material material = materialRepository.selectOne(
                new LambdaQueryWrapper<Material>()
                        .eq(Material::getId, materialId)
                        .eq(Material::getUserId, userId)
                        .eq(Material::getStatus, 1));

        if (material == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "素材不存在");
        }

        // 增加下载次数
        materialRepository.incrementDownloadCount(materialId);

        return fileUtil.generateDownloadUrl(material.getFilePath());
    }

    /**
     * 转换为DTO
     */
    private MaterialDTO convertToDTO(Material material) {
        MaterialDTO dto = new MaterialDTO();
        BeanUtils.copyProperties(material, dto);

        // 获取标签列表
        List<Long> tagIds = materialTagRepository.selectTagIdsByMaterialId(material.getId());
        if (!CollectionUtils.isEmpty(tagIds)) {
            List<Tag> tags = tagRepository.selectBatchIds(tagIds);
            List<TagDTO> tagDTOs = tags.stream().map(tag -> {
                TagDTO tagDTO = new TagDTO();
                BeanUtils.copyProperties(tag, tagDTO);
                return tagDTO;
            }).collect(Collectors.toList());
            dto.setTags(tagDTOs);
        }

        return dto;
    }
}